#include <gtkmm-2.4/gtkmm.h>
#include <glibmm-2.4/glibmm.h>
#include <gdkmm-2.4/gdkmm.h>
#include <gdkmm-2.4/gdkmm/pixbuf.h>
#include <iostream>
#include <deque>
#include <string>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include "captionWindow.cpp"
#include "catImage.cpp"
#include "quickbrowse.cpp"
#include "labelFactory.cpp"
using namespace std;
class mbrowser : public Gtk::Window
{
	
	protected:
		Glib::ustring imagename;
		Glib::ustring dirname;
		Gtk::Button m_button;
		Gtk::Button blur_button;
		Gtk::HBox box;
		Gtk::HBox box2;
		Gtk::VBox box3;
		Gtk::HBox box4;
		Gtk::Image image;
		Gtk::Button next_button;
		Gtk::Button back_button;
		Gtk::Button scale_button;
		Gtk::Button caption_button;
		Gtk::ScrolledWindow sim;
		Gtk::Entry title;
		Gtk::Entry tags;
		Gtk::Label titleLabel;
		Gtk::Label tagLabel;
		deque<catImage> imageList;
		deque<char*> files;
		deque<Glib::ustring> tr;
		Glib::RefPtr<Gdk::Pixbuf> i;
		int cindex; //current index for tr
		int ow, oh;
		bool scaled;
		boost::thread *open_thread;
		boost::thread *watermark_thread;
		boost::thread *thumbthread;
		boost::thread *captionThread;
		//Gtk::VBox windowBox;


		Gtk::ProgressBar loadProgress;

		//places the current image from imageList into the viewport
		void addImage()
		{
			image.clear();
			i = imageList[cindex].getPixbuf();
			if(scaled)
			{
				if(!imageList[cindex].hasScaled())
				{
					imageList[cindex].fitInto(
							sim.get_width(),
							sim.get_height());
				}
				i = imageList[cindex].getScaledInstance();
			}
			image.set(i);
			title.set_text(imageList[cindex].getTitle());
			tags.set_text(imageList[cindex].getTagsAsString());
		}

		//does whatever the button is set to do, originally to blur
		//the image, but at the moment is actually showing the preview
		//window from quickbrowse.cpp
		void showQuickbrowse()
		{
			cout<<"trying to join threads"<<endl;
			if(open_thread!=NULL)
				open_thread->join();


			if(captionThread!=NULL)
				captionThread->join();


			cout<<"threads joined"<<endl;
			photoBook p(imageList,5,5,cindex);
			Gtk::Main::run(p);
			short r = p.run();
			cout<<"running r"<<endl;
			if(r==photoBook::IMAGE_SELECTION_CANCEL)
			{
			}
			else if(r < 0 || r > imageList.size()-1)
			{
				cerr<<"error: r not between 0 and size"<<endl;
			}
			else
			{
				imageList[cindex].setTitle(title.get_text());
				imageList[cindex].addTags(tags.get_text());
				cindex = (int)r;
			}
			addImage();
	
		}

		/*
		 *
		 * Watermarks all images with s
		 *
		 */

		void batchWatermark(string s)
		{
			/*register int co = cindex;
			for(register int x=0;x<imageList.size();x++)
			{
				imageList[cindex++].caption(makeCaption(s));
			}
			cindex = co;
			addImage();
			
			*/
			imageList[cindex].caption(makeCaption(s));
			addImage();
			captionThread = new boost::thread(
					boost::bind(&mbrowser::tBatchWatermark,
						this,
						s));
		}

		void tBatchWatermark(string s)
		{
			int co = cindex;
			for(int x=0;x<co;x++)
			{
				imageList[cindex++].caption(makeCaption(s));
			}
			for(int x=co+1;x<imageList.size()-1;x++)
			{
				imageList[cindex++].caption(makeCaption(s));
			}
		}
		
		/*
		 *
		 * looks at the watermark or annotation string
		 * and performs a simple replacement
		 *
		 */

		string makeCaption(string ctext)
		{
			string capt = ctext;
			if(ctext[0]=='$')
			{
				if(ctext=="$title")
				{
					capt=imageList[cindex].getTitle();
				}
				else if(ctext=="$tags")
				{
					capt=imageList[cindex].
						getTagsAsString();
				}
			}

			return capt;
		}

		/*
		 *
		 * This function annotates the image
		 *
		 */
		void doCaption()
		{
			imageList[cindex].setTitle(title.get_text());
			imageList[cindex].addTags(tags.get_text());
			captionWindow* cw = new captionWindow();
			Gtk::Main::run(*cw);
			cw->run();
			string ctext = cw->getCaptionString();
			unsigned short cwrt = cw->getCaptionType();
			switch(cwrt)
			{
				case captionWindow::CAPTION_WINDOW_SINGLE:
					//imageList[cindex].
					//	caption(makeCaption(ctext));
					imageList[cindex].
						makeAnnotation(
							makeCaption(ctext));
					break;
				case captionWindow::CAPTION_WINDOW_ALL:
					
					watermark_thread = 
						new boost::thread(
							boost::bind(
								&mbrowser::
								batchWatermark,
								this,
								ctext
								)
							);

					//batchWatermark(ctext);
					break;
				default:
					break;
			}
			delete cw;
			addImage();
		}

		/*
		 *
		 * performs the actual scaling of the image for the 
		 * scaleImage() function
		 * if fr is set to true, then it forces the image to 
		 * generate a new
		 * thumbnail instead of using the cached version.
		 *
		 */	
		inline Glib::RefPtr<Gdk::Pixbuf> doScale(bool fr=false)
		{
			imageList[cindex].fitInto(
						sim.get_width()-20,
						sim.get_height()-20,
						fr);
			return imageList[cindex].getScaledInstance();
		}

		Glib::RefPtr<Gdk::Pixbuf> doScaleImg(unsigned int cidx, 
							bool fr=false)
		{

			imageList[cidx].fitInto(
						sim.get_width()-20,
						sim.get_height()-20,
						fr);
			return imageList[cidx].getScaledInstance();
		}

		/*
		 *
		 * This function first checks the current window size, and
		 * the last size of the window.  If the window size has changed
		 * then we fit the image into the current windowsize.
		 *
		 * If the window size is the same as last time, then we check
		 * to see if we are currently in the thumbnail view.  If we are
		 * in the full view, then we scale it, 
		 * otherwise we show the full
		 * image.
		 *
		 */
		void scaleImage()
		{
			scaled = !scaled;
			Glib::RefPtr<Gdk::Pixbuf> t;
			if((ow!=this->get_width()) && (oh!=this->get_height()))
			{
				ow = this->get_width();
				oh = this->get_height();
				t=doScale(true);
				scaled = true;
			}
			else if(!scaled)
			{
				ow = this->get_width();
				oh = this->get_height();
				t=doScale(false);
			}
			else
			{
				cout<<"inflating image"<<endl;
				t = i;
			}
			image.set(t);
			addImage();

		}

		void backgroundImageScale()
		{
			scaled = !scaled;
			Glib::RefPtr<Gdk::Pixbuf> t;
			for(register int x=0;x<imageList.size()-1;x++)
			{

				if((ow!=this->get_width()) 
						&& (oh!=this->get_height()))
				{
					ow = this->get_width();
					oh = this->get_height();
					t=doScaleImg(x,true);
					scaled = true;
				}
				else if(!scaled)
				{
					ow = this->get_width();
					oh = this->get_height();
					t=doScaleImg(x,false);
				}
				else
				{
					cout<<"inflating image"<<endl;
					t = i;
				}
				//image.set(t);
				//addImage();
			}
		}

		//looks at the next image in the imageList
		void cycleLeft()
		{
			imageList[cindex].setTitle(title.get_text());
			imageList[cindex].addTags(tags.get_text());

			if(cindex==0)
			{
				cindex = imageList.size()-1;
			}
			else
			{
				cindex--;
			}
			
			addImage();
		}

		//looks at the next image in the imageList
		void cycleRight()
		{
			imageList[cindex].setTitle(title.get_text());
			imageList[cindex].addTags(tags.get_text());

			if(cindex>=imageList.size()-1)
			{
				cindex=0;
			}
			else
			{
				cindex++;
			}
			
			addImage();
		}

		//starts looking at the first image in a directory
		//after having loaded a new directory full of images
		void addImageDir()
		{
			cindex=0;
			addImage();
		}


		//handles the file chooser dialog
		//as well as actually opening the
		//image directory and looking at the
		//files in it, and deciding what files
		//are image files, and putting those into the imageList
		virtual void fileButtonClicked()
		{
			Gtk::FileChooserDialog dialog("Please select an Image",
			Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
			Gtk::FileFilter image_filter;
			image_filter.set_name("Image Files");
			image_filter.add_mime_type("image/jpeg");
			image_filter.add_mime_type("image/pjpeg");
			image_filter.add_mime_type("image/gif");
			image_filter.add_mime_type("image/png");
			image_filter.add_mime_type("image/pict");
			image_filter.add_mime_type("image/tiff");
			image_filter.add_mime_type("image/x-tiff");

			dialog.add_filter(image_filter);
			dialog.set_transient_for(*this);
			dialog.add_button(
					Gtk::Stock::CANCEL,
				       	Gtk::RESPONSE_CANCEL);

			dialog.add_button("Select", Gtk::RESPONSE_OK);
			
			int result = dialog.run();
			
			switch(result)
			{
				case(Gtk::RESPONSE_CANCEL):
					break;
				case(Gtk::RESPONSE_OK):
					dirname = dialog.get_filename();
					loadProc();
					break;
			}

			m_button.set_label(Glib::ustring("Add Directory"));

		}

		void loadProc()
		{
			Glib::Dir dl(dirname);
			tr = deque<Glib::ustring>
				(dl.begin(), dl.end());
			
			//if(catImage::checkType(tr[x],
			//	dirname))
			for(int x=-1;x<2 && tr.size()>0;x++)
			{
				while(!catImage::checkType(tr[x],dirname)
						&& tr.size()>0)
				{
					imageList.push_back(
						catImage(tr[x],
						dirname));
					tr.pop_front();
				}
			}
			if(tr.size()>0)
			{
				open_thread = new boost::thread(boost::bind(
							&mbrowser::
							background_load,
							this));
			}
			
			/*x=1;
			if(catImage::checkType(tr[x],
				dirname))
			{
				imageList.push_back(
					catImage(tr[x],
					dirname));
				tr.pop_front();
			}
			x=tr.size()-1;
			if(catImage::checkType(tr[x],
				dirname))
			{
				imageList.push_back(
					catImage(tr[x],
					dirname));
				tr.pop_back();
			}*/

			next_button.show();
			back_button.show();
			scale_button.show();
			addImageDir();
		}

		void background_load()
		{
			loadProgress.set_fraction(0);
			loadProgress.show();
			double tl=3;
			while(tr.size()>0)
			{
				cout<<"checking: "<<dirname<<"/"<<tr[0]<<endl;
				if(catImage::checkType(tr[0],dirname))
				{
					imageList.push_back(
						catImage(tr[0],
						dirname));
				}
				double ms=tr.size()+2;
				double fra=tl/ms;
				cout<<"setting completion fraction to: "<<
					fra<<endl;

				loadProgress.set_fraction(fra);
				tr.pop_front();
			}

			loadProgress.set_fraction(0);
			loadProgress.hide();
		}

		void loadImageByNum(int x)
		{
		}
	
	public:
		//constructor
		mbrowser() : 
			m_button("Select Image Directory"),
			next_button(">"),
			back_button("<"),
			cindex(0),
			scale_button("Zoom"),
			scaled(false),
			blur_button("Preview"),
			caption_button("Caption"),
			titleLabel("Title"),
			tagLabel("Tags")
		{
			catImage ci("f-spot-logo.png","/usr/share/pixmaps");

			Glib::RefPtr<Gdk::Pixbuf> i = ci.getPixbuf();

			image.set(i);

			set_border_width(10);
			m_button.signal_clicked().
				connect(sigc::mem_fun(	*this, 
							&mbrowser::
							fileButtonClicked));

			next_button.signal_clicked().
				connect(sigc::mem_fun(	*this, 
							&mbrowser::
							cycleRight));


			back_button.signal_clicked().
				connect(sigc::mem_fun(	*this,
						       	&mbrowser::
							cycleLeft));

			caption_button.signal_clicked().
				connect(sigc::mem_fun(
							*this,
							&mbrowser::
							doCaption));

			add(box);
			scale_button.signal_clicked().
				connect(sigc::mem_fun(	*this, 
							&mbrowser::
							scaleImage));

			title.set_width_chars(25);

			blur_button.signal_clicked().
				connect(sigc::mem_fun(	*this,
							&mbrowser::
							showQuickbrowse));


			box4.pack_start(m_button,Gtk::PACK_SHRINK,FALSE);
			box4.pack_start(scale_button,Gtk::PACK_SHRINK,0);
			box4.pack_start(back_button,Gtk::PACK_SHRINK,FALSE);	
			box4.pack_start(next_button,Gtk::PACK_SHRINK,FALSE);
			box3.pack_start(box4,Gtk::PACK_SHRINK,0);
			box3.pack_start(blur_button,Gtk::PACK_SHRINK,3);
			box3.pack_start(caption_button,Gtk::PACK_SHRINK,2);
			box3.pack_start(titleLabel,Gtk::PACK_SHRINK,0);
			box3.pack_start(title,Gtk::PACK_SHRINK,0);
			box3.pack_start(tagLabel,Gtk::PACK_SHRINK,0);
			box3.pack_start(tags,Gtk::PACK_SHRINK,5);

			box3.pack_end(loadProgress,Gtk::PACK_SHRINK,5);

			box.pack_start(box3,Gtk::PACK_SHRINK,0);
			box.pack_start(box2,Gtk::PACK_EXPAND_WIDGET,0);
			sim.add(image);
			box2.pack_start(sim,Gtk::PACK_EXPAND_WIDGET,0);
		
			sim.set_size_request(i->get_width()*2,
		     		i->get_height()*2);
		
			sim.set_policy(Gtk::POLICY_AUTOMATIC,
				Gtk::POLICY_AUTOMATIC);

			titleLabel.show();
			tagLabel.show();
			blur_button.show();
			caption_button.show();
			box.set_border_width(5);
			box2.set_border_width(5);
			tags.show();
			sim.show();
			box2.show();
			box.show();
			m_button.show();
			image.show();
			box3.show();
			box4.show();
			title.show();
			//loadProgress.set_fraction(0);
			//loadProgress.show();
		}
	
		//destructor	
		virtual ~mbrowser(){}

		//goes to a certain image in the imageList
		void jumpTo(int cid)
		{
			cindex = cid;
		       	addImage();
		}
};

//main, for testing
int main(int argc, char*argv[])
{
	Gtk::Main kit(argc, argv);
	mbrowser hworld;
	Gtk::Main::run(hworld);	
	return 0;
}
